1 module hip.hipaudio.backend.xaudio.source; 2 3 version(Windows): 4 version(DirectX): 5 6 import hip.hipaudio.backend.xaudio.player; 7 import hip.hipaudio.backend.xaudio.clip; 8 import hip.hipaudio.backend.audioclipbase; 9 import hip.hipaudio.audiosource; 10 import directx.xaudio2; 11 import directx.win32; 12 import hip.util.system:getWindowsErrorMessage; 13 import hip.error.handler; 14 15 enum WAVE_FORMAT_IEEE_FLOAT = 0x0003; 16 17 class HipXAudioSource : HipAudioSource 18 { 19 IXAudio2SourceVoice sourceVoice; 20 protected bool isClipDirty = true; 21 22 this(HipXAudioPlayer player) 23 { 24 25 AudioConfig cfg = player.config; 26 WAVEFORMATEX fmt; 27 WORD format; 28 29 switch(cfg.getBitDepth) 30 { 31 case 8: 32 case 16: 33 format = WAVE_FORMAT_PCM; 34 break; 35 case 32: 36 format = WAVE_FORMAT_IEEE_FLOAT; 37 break; 38 default: 39 ErrorHandler.assertExit(false, "XAudio2 Does not support the current bit depth"); 40 break; 41 } 42 fmt.wFormatTag = format; 43 fmt.nChannels = cast(ushort)cfg.channels; 44 fmt.nAvgBytesPerSec = cfg.sampleRate * (cfg.getBitDepth/8) * cfg.channels; 45 fmt.nSamplesPerSec = cfg.sampleRate; 46 fmt.nBlockAlign = cast(ushort)(cfg.channels * cfg.getBitDepth())/8; 47 fmt.wBitsPerSample = cast(ushort)cfg.getBitDepth; 48 fmt.cbSize = 0; 49 HRESULT hr = player.xAudio.CreateSourceVoice(&sourceVoice, &fmt); 50 51 ErrorHandler.assertLazyExit(SUCCEEDED(hr), "Could not create source voice: \n\t"~HipXAudioPlayer.getError(hr)); 52 53 } 54 alias clip = HipAudioSource.clip; 55 56 57 protected void submitClip() 58 { 59 XAUDIO2_BUFFER* buffer = getBufferFromAPI(clip).xaudio; 60 if(isClipDirty) 61 { 62 isClipDirty = false; 63 HRESULT hr = sourceVoice.SetSourceSampleRate(clip.getSampleRate()); 64 ErrorHandler.assertLazyExit(SUCCEEDED(hr), 65 "Could not set source voice Sample Rate:\n\t"~HipXAudioPlayer.getError(hr)); 66 } 67 68 HRESULT hr = sourceVoice.SubmitSourceBuffer(buffer, null); 69 ErrorHandler.assertLazyExit(SUCCEEDED(hr), 70 "Could not submit XAudio2 source voice:\n\t"~HipXAudioPlayer.getError(hr)); 71 } 72 73 override IHipAudioClip clip(IHipAudioClip newClip) 74 { 75 if(newClip != clip) 76 isClipDirty = true; 77 super.clip(newClip); 78 return newClip; 79 } 80 81 alias loop = HipAudioSource.loop; 82 override bool loop(bool value) 83 { 84 bool ret = super.loop(value); 85 HipXAudioClip c = clip.getAudioClipBackend!(HipXAudioClip); 86 c.buffer.LoopCount = loop ? XAUDIO2_LOOP_INFINITE : 0; 87 return ret; 88 } 89 90 91 92 override bool play() 93 { 94 if(isPlaying) 95 { 96 sourceVoice.Stop(); 97 sourceVoice.FlushSourceBuffers(); 98 } 99 submitClip(); 100 101 HRESULT hr = sourceVoice.Start(0); 102 ErrorHandler.assertLazyExit(SUCCEEDED(hr), "XAudio2 Failed to play: \n\t"~HipXAudioPlayer.getError(hr)); 103 isPlaying = true; 104 return SUCCEEDED(hr); 105 } 106 override bool stop() 107 { 108 //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too. 109 auto hr = sourceVoice.Stop(XAUDIO2_PLAY_TAILS); 110 ///Makes it return to 0 111 sourceVoice.FlushSourceBuffers(); 112 debug 113 ErrorHandler.assertLazyErrorMessage(SUCCEEDED(hr), "XAudio2 stop failure", HipXAudioPlayer.getError(hr)); 114 115 isPlaying = false; 116 return SUCCEEDED(hr); 117 } 118 override bool pause() 119 { 120 isPaused = true; 121 //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too. 122 return SUCCEEDED(sourceVoice.Stop(XAUDIO2_PLAY_TAILS)); 123 } 124 override bool play_streamed() => false; 125 126 127 ~this() 128 { 129 if(sourceVoice !is null) 130 { 131 sourceVoice.DestroyVoice(); 132 sourceVoice = null; 133 } 134 } 135 }